/*!
 * @Title：layui-ko
 * @Version: 0.0.1
 * @Description：基于knockout.js的 layui 绑定库
 * @Author: 一只小白猿
 * @License: MIT
 * @Project: https://gitee.com/haodong108/layui-ko
 */

/***************
   引入绑定模型     
***************/
import LayDateRangeVmodel from "./modules/LayDateRangeVmodel.js";
import LayDateVmodel from "./modules/LayDateVmodel.js";
import LayRadioVmodel from "./modules/LayRadioVmodel.js";
import LaySelectVmodel from "./modules/LaySelectVmodel.js";
import LaySliderVmodel from "./modules/LaySliderVmodel.js";
import LayRateVmodel from "./modules/LayRateVmodel.js";
import LayTransferVmodel from "./modules/LayTransferVmodel.js";
import LayInputVmodel from "./modules/LayInputVmodel.js";
import XmSelectVmodel from "./modules/XmSelectVmodel.js";
import LayProgressVmodel from "./modules/LayProgressVmodel.js";
import { LayCheckBoxVmodel, CheckedArrayAccessor } from "./modules/LayCheckBoxVmodel.js"
import { logger, tools, runtime } from "./modules/tools.js";
import LayVmodelBase from "./modules/LayVmodelBase.js";
import LayTableVmodel from "./modules/LayTableVmodel.js";

(function () {
    if (!layui) {
        console.error("必须引入layui");
        return;
    }

    //开启 console.debug
    if (layui.cache.debug) {
        console.debug = console.log;
    }

    function tryCallbindingHandler(funcName, tis, args) {
        var bindingHandler = eval("ko.bindingHandlers." + funcName);
        if (bindingHandler && typeof bindingHandler === "function") {
            return bindingHandler.apply(tis, ...args);
        }
    }

    function AccessorObjParse(element, valueAccessor) {    
        var res = {};
        var accessor = valueAccessor();

        if (!ko.isObservable(accessor)) {
            let obj = valueAccessor();
            if (typeof obj == "object") {
                for (let key in obj) {
                   res[key] = obj[key];
                }
            }
            else {
                throw new Error("无法解析绑定值");
            }
        }
        else {
            res.value = accessor;
        }

        if (!res.config && $(element).attr("lay-options")) {
            let layOption = $(element).attr("lay-options");
            res.config = eval("(" + layOption + ")");
        }

        return res;
    }

    /**元素-模型查找映射器 */
    var elemMapper = {
        /**vmodel 匹配规则 */
        matchRules: [
            {
                type: LaySelectVmodel,
                match: function (elem) {
                    var $next = $(elem).next();
                    return $next.hasClass("layui-form-select");
                }
            },
            {
                type: LayRadioVmodel,
                match: function (elem) {
                    var $next = $(elem).next();
                    return $next.hasClass("layui-form-radio");
                }
            },
            {
                type: LayCheckBoxVmodel,
                match: function (elem) {
                    var $next = $(elem).next();
                    return $next.hasClass("layui-form-checkbox") || $next.hasClass("layui-form-switch");
                }
            },
            {
                type: LayProgressVmodel,
                match: function (elem) {
                    return $(elem).hasClass("layui-progress");
                }
            },
            {
                type: LayTableVmodel,
                match: function (elem) {
                    return $(elem).is("table");
                }
            },
            {
                type: LayInputVmodel,
                match: function (elem) {
                    return $(elem).is('input');
                }
            }
        ],
        /**
         * 获取表单元素关联的的LayModel
         * @param {Element} elem 
         * @returns {LayVmodelBase | LayCheckBoxVmodel | LaySelectVmodel | LayRadioVmodel | LayDateVmodel | LayDateRangeVmodel}
         */
        matchElemLayModel: function (elem) {
            var tis = this;
            var $next = $(elem).next();

            if (elem.layModel) return elem.layModel;

            var match = tis.matchRules.find((rule) => {
                return rule.match(elem);
            });

            if (match) {
                return new match.type(elem);
            }
        },
        /**
         * 获取模型
         * @return {LayVmodelBase}
         * */
        getLayModel: function (elem) {
            return elem.layModel;
        },
        setLayModel: function (elem, layModel) {
            elem.layModel = layModel;
        }
    }

    /**
     * ko绑定 LayDate
     */
    ko.bindingHandlers.laydate = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var bind = AccessorObjParse(element, valueAccessor);

            //单日期
            if ($(element).is('input[type="text"]')) {          
                var value = bind.value;
                var config = bind.config;

                //如果 value 不为字符串，则抛出异常
                var value = ko.unwrap(value);
                if (value && typeof value !== "string") {
                    throw new Error("laydate绑定值必须是string类型");
                }

                var layModel = elemMapper.getLayModel(element) || new LayDateVmodel(element, config);
                elemMapper.setLayModel(element, layModel);
                layModel.regVItem("value", value);
                layModel.init();
            }
            //日期范围
            else if ($(element).is('div')) {
                var begin = bind.begin;
                var end = bind.end;
                var config = bind.config;
                let isObs = ko.isObservable(valueAccessor);                
                if (isObs || (!isObs && typeof valueAccessor() !== 'object')) {
                    throw new Error("laydate绑定表达式必须是object类型且需要包含begin,end属性");
                }

                var layModel = elemMapper.getLayModel(element) || new LayDateRangeVmodel(element, config);
                elemMapper.setLayModel(element, layModel);

                layModel.regVItem("begin", begin);
                layModel.regVItem("end", end);
                layModel.init();

                //接管后代绑定
                //return { controlsDescendantBindings: true };
            }
            else {
                throw new Error('目标元素不支持laydate绑定');
            }
        }
    }

    /**
     * layvalue, 可直接绑定拥有value值的 layModel组件
     */
    ko.bindingHandlers.layvalue = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var value = valueAccessor();
            var accessorValue = ko.unwrap(valueAccessor);
            var layModel = elemMapper.matchElemLayModel(element);
            if (layModel && layModel.vm.value != void 0) {
                //从allBindingsAccessor 查找是否有 valevent 属性
                var valueEvent = allBindingsAccessor().valueEvent;
                if (valueEvent && layModel instanceof LayInputVmodel) {
                    layModel.valueEvent = valueEvent;
                }

                layModel.regVItem("value", value);
                layModel.init();
            }
        }
    }

    /**
     * ko绑定 LayCheckBox
     */
    ko.bindingHandlers.laychecked = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var accessor = valueAccessor();
            var accessorValue = ko.unwrap(accessor);
            if (!['INPUT'].includes(element.tagName)) {
                throw new Error("laychecked 绑定元素必须是input");
            }
            var layModel = elemMapper.matchElemLayModel(element);

            /**复选框 */
            if (layModel && layModel instanceof LayCheckBoxVmodel) {

                if (typeof accessor === "function") {
                    if (typeof accessorValue === "boolean") {
                        layModel.regVItem("checked", accessor);
                    }
                    //判断 accessorValue 是否为数组
                    else if (Array.isArray(accessorValue)) {
                        if (accessorValue.length > 0) {
                            if (typeof accessorValue[0] !== "string") {
                                throw new Error(`checkbox,数组值类型必须是string`);
                            }
                        }
                        let id = tools.hash.hashCode(accessor);
                        var arrAccessor = runtime.layCheckedSlot.getAACbyId(id);
                        if (!arrAccessor) {
                            arrAccessor = new CheckedArrayAccessor(id, accessor);
                            runtime.layCheckedSlot.checkArrayAccessors.push(arrAccessor);
                        }
                        arrAccessor.addLayCheckbox(layModel);
                    }
                    else {
                        throw new Error("不支持的可观察对象类型");
                    }
                }
                else if (typeof accessor === "boolean") {
                    layModel.vm.checked(accessor);
                }
                else {
                    throw new Error("laychecked-checkbox 绑定值必须是function,boolean类型");
                }
            }

            /**单选框 */
            else if (layModel && layModel instanceof LayRadioVmodel) {
                if (typeof accessor !== "function") {
                    throw new Error("laychecked-radio 绑定值必须是可观察对象类型");
                }
                if (typeof accessorValue !== "string") {
                    throw new Error("laychecked-radio 可观察对象类型必须是string类型");
                }
                //绑定值改变时，同步到单选值
                accessor.subscribe(function (newValue) {
                    layModel.vm.checked(newValue === layModel.vm.value());
                });
                //单选值改变时，同步到绑定值
                layModel.vm.checked.subscribe(function (newValue) {
                    if (newValue === true) {
                        accessor(layModel.vm.value());
                    }
                });

                layModel.vm.checked(accessorValue === layModel.vm.value());
            }

            else {
                throw new Error("laychecked 不支持元素类型" + element.type);
            }

            layModel.init();

        }
    }

    /**
     * layui 控制可用性
     */
    ko.bindingHandlers.laydisable = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var accessor = valueAccessor();
            var accessorValue = ko.unwrap(accessor);
            if (!['boolean', 'function'].includes(typeof accessor)) {
                throw new Error("laydisable 绑定值必须是boolean,或可观察类型");
            }

            var layModel = elemMapper.matchElemLayModel(element);
            if (!layModel) return;

            layModel.regVItem("disabled", accessor);
            layModel.init();
        }
    }

    /**
     * layui 滑块
     */
    ko.bindingHandlers.layslider = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var bind = AccessorObjParse(element, valueAccessor);
            var value = bind.value;
            var begin = bind.begin;
            var range = bind.range;
            var end = bind.end;
            var config = bind.config;

            var layModel = elemMapper.getLayModel(element) || new LaySliderVmodel(element, config || {});
            elemMapper.setLayModel(element, layModel);
            if (!config.range) {
                layModel.regVItem("value", value);
            } else {
                layModel.regVItem("begin", begin);
                layModel.regVItem("end", end);
                layModel.regVItem("range", range);
            }

            layModel.init();
        }
    }

    /**
     * layui 评分
     */
    ko.bindingHandlers.layrate = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var bind = AccessorObjParse(element, valueAccessor);
            var value = bind.value;
            var config = bind.config; 

            var layModel = elemMapper.getLayModel(element) || new LayRateVmodel(element, config || {});
            elemMapper.setLayModel(element, layModel);
            layModel.regVItem("value", value);
            layModel.init();
        }
    }

    /**
     * layui 表格
     */
    ko.bindingHandlers.laytable = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var bind = AccessorObjParse(element, valueAccessor);
            var data = bind.data;
            var config = bind.config;

            var layModel = elemMapper.getLayModel(element) || new LayTableVmodel(element, config || {},data);
            elemMapper.setLayModel(element, layModel);
            layModel.regVItem("data", data);
            layModel.init();
        }
    }

    //单向绑定组件
    /**
     * transfer 
     */
    ko.bindingHandlers.laytransfer = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var bind = AccessorObjParse(element, valueAccessor);
            var value = bind.value;
            var config = bind.config;

            var layModel = elemMapper.getLayModel(element) || new LayTransferVmodel(element, config || {});
            elemMapper.setLayModel(element, layModel);
            layModel.regVItem("value", value);

            layModel.init();
        }
    }


    //layui生态 第三方组件
    /**
     * xm-select
     */
    ko.bindingHandlers.xmselect = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            if (!xmSelect) {
                throw new Error("未引入xm-select.js");
            }
            var bind = AccessorObjParse(element, valueAccessor);
            var value = bind.value;
            var config = bind.config;          

            var layModel = elemMapper.getLayModel(element) || new XmSelectVmodel(element, config || {});
            elemMapper.setLayModel(element, layModel);
            layModel.regVItem("value", value);

            layModel.init();
        }
    }
})()
